﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CAEXObjectViewModel.cs" >
//   J. Prinz
// </copyright>
// <summary>
//   UI Representation of any CAEX Object, the Implementation of IDataErrorInfo asserts, that each CAEX Object has at least a Name
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace AutomationMLMapper.ViewModels.CAEXObjectViewModels
{
    using System;
    using System.ComponentModel;
    using System.IO;
    using System.Xml.Linq;

    using AMLEngineExtensions;

    using CAEX_ClassModel;

    using AutomationMLMapper.AutomationML.Extensions;

    /// <summary>
    ///  UI Representation of any CAEX Object, the Implementation of IDataErrorInfo asserts, that each CAEX Object has at least a Name
    /// </summary>
    /// <typeparam name="T">
    ///  could be any CAEX-Object
    /// </typeparam>
    [DisplayName("CAEXObject")]
    public class CAEXObjectViewModel<T> : ICAEXObject, IDataErrorInfo
        where T : CAEXBasicObject
    {
        /// <summary>
        ///  <see cref="ContainerViewModel"/>
        /// </summary>
        private ICAEXObject containerViewModel;

        // ReSharper restore StaticFieldInGenericType
        #region Constructors and Destructors

        /// <summary>
        /// Initializes static members of the <see cref="CAEXObjectViewModel{T}"/> class. 
        /// </summary>
        static CAEXObjectViewModel()
        {
            CAEXObjectMetaData = new MetaObjectInformation
                {
                   WriterID = "AML21FB8FE3-E1E4-44E1-BD16-2D5320775CBE", WriterName = "AutomationMLMapper" 
                };
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CAEXObjectViewModel{T}"/> class. 
        /// </summary>
        public CAEXObjectViewModel()
        {
            this.OriginalNode = null;
        }

        /// <summary>
        ///  Initializes a new instance of the <see cref="CAEXObjectViewModel{T}"/> class. 
        /// </summary>
        /// <param name="caexObject">
        ///  a CAEX-Element 
        /// </param>
        public CAEXObjectViewModel(T caexObject)
            : this()
        {
            // ReSharper disable DoNotCallOverridableMethodsInConstructor
            this.CaexObject = caexObject;
            // ReSharper restore DoNotCallOverridableMethodsInConstructor
        }

        #endregion

        #region Public Events

        /// <summary>
        ///   PropertyChanged Event
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Public Properties

        /// <summary>
        ///   DisplayName used in the UI in headers and labels
        /// </summary>
        public static string DisplayName
        {
            get
            {
                return "CAEXObject";
            }
        }

        /// <summary>
        ///   ParameterType of the Class (this is the Type of a CAEX-Element from the CAEX_ClassModel <see cref="CAEX_ClassModel" />)
        /// </summary>
        public static Type ParameterType
        {
            get
            {
                return typeof(T);
            }
        }

        /// <summary>
        /// Gets the CaexObject.
        /// </summary>
        public virtual T CaexObject { get; protected set; }

        /// <summary>
        /// Gets Default Error
        /// </summary>
        public string Error
        {
            get
            {
                return this[string.Empty];
            }
        }

        /// <summary>
        /// Gets the CaexObject Path
        /// </summary>
        public string FullPath
        {
            get
            {
                try
                {
                    return (this.CaexObject != null) ? this.CaexObject.CAEXPath() : string.Empty;
                }
                catch
                {
                    return string.Empty;
                }
            }
        }

        /// <summary>
        ///   Validation result, set by the Methods of IDataErrorInfo
        /// </summary>
        public virtual bool IsValid { get; set; }

        /// <summary>
        ///   ContainerViewModel of the CaexObject. This is the ViewModel of the Container, which contains the representation for the CaexObject
        /// </summary>
        public ICAEXObject ContainerViewModel
        { 
            get
            {
                return this.containerViewModel;
            }

            set
            {
                if (value != null)
                {
                    this.containerViewModel = value;
                    this.OnNotifyPropertyChanged("ContainerViewModel");
                    this.OnNotifyPropertyChanged("ContainerName");

                   // this.containerViewModel.PropertyChanged += this.ContainerViewModelPropertyChanged;
                }
            }
        }

        /// <summary>
        ///  Name of the Container, which contains a representation of the CaexObject
        /// </summary>
        public string ContainerName
        {
            get
            {
                return (this.ContainerViewModel != null) ? this.ContainerViewModel.Name : string.Empty;
            }
        }

        /// <summary>
        ///   Name of the CaexObject
        /// </summary>
        public virtual string Name
        {
            get
            {
                if (this.CaexObject is CAEXObject)
                {
                    var caex = this.CaexObject as CAEXObject;
                    return caex.Name();
                }

                return this.CaexObject != null ? this.CaexObject.Name() : string.Empty;
            }

            set
            {
                if (this.CaexObject is CAEXObject)
                {
                    var caex = this.CaexObject as CAEXObject;
                    if (!caex.Name.Exists() || caex.Name.Value != value)
                    {
                        caex.Name.Value = value;

                        // needs validation
                        this.IsValid = false;
                        this.OnNotifyPropertyChanged("Name");
                        this.OnNotifyPropertyChanged("FullPath");
                    }
                }
            }
        }

        /// <summary>
        ///   CaexObject Parents Name (The Ancestor in the Inheritance Tree of the CAEXObject)
        /// </summary>
        public string ParentName
        {
            get
            {
                string parentName;

                try
                {
                    parentName = (this.CaexObject is IClassWithBaseClassReference
                              && (this.CaexObject as IClassWithBaseClassReference).GetRefBaseClassPath != null)
                                 ? Path.GetFileName(
                                     (this.CaexObject as IClassWithBaseClassReference).GetRefBaseClassPath.Value)
                                 : string.Empty;
                }
                catch
                {
                    // Engine Method throws exception if no ClassPath Value exists
                    parentName = null;
                }

                return parentName;
            }
        }

        /// <summary>
        /// Gets or sets the Library which contains the Caex-Object.
        /// </summary>
        public CAEXBasicObject LibraryCaexObject { get; set; }

        /// <summary>
        /// Gets the Name of the <see cref="LibraryCaexObject"/>.
        /// </summary>
        public string LibraryName
        {
            get
            {
                return (this.LibraryCaexObject != null) ? ((CAEXObject)this.LibraryCaexObject).Name.Value : string.Empty;
            }
        }

        #endregion

        /// <summary>
        ///   static Meta Data Object assignable to all CAEXObjects, created from the AutomationMLMapper
        /// </summary>
        // ReSharper disable StaticFieldInGenericType
        protected static MetaObjectInformation CAEXObjectMetaData { get; set; }

        #region Properties

        /// <summary>
        ///   The OriginalNode is calculated from the XmlNode of the CAEXObject after loading the CaexObject in the
        ///   Representation Model - this value is used to determine the ChangeState of the CAEXObject
        /// </summary>
        protected XNode OriginalNode { get; set; }

        #endregion

        #region Public Indexers

        /// <summary>
        /// Text for any error, associated with a specific Property
        /// </summary>
        /// <param name="propertyName">
        /// Name of the Property 
        /// </param>
        /// <returns>
        /// A String which is displayed as an Error Notification 
        /// </returns>
        public virtual string this[string propertyName]
        {
            get
            {
                this.IsValid = false;
                switch (propertyName)
                {
                    case "ContainerName":
                        if (string.IsNullOrEmpty(this.ContainerName))
                        {
                            return "Please enter a Name for the Container";
                        }

                        break;

                    case "Name":
                        if (string.IsNullOrEmpty(this.Name))
                        {
                            return "Please enter a Name for the CAEX-Object";
                        }

                        break;
                }

                this.IsValid = true;
                return null;
            }
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// Method called, when any child is added to the Caex-Object
        /// </summary>
        /// <param name="caexObjectType">
        /// Type of the representation ViewModel of the added CAEX-Object 
        /// </param>
        /// <returns>
        /// The AutomationMLMapper.ViewModels.ICAEXObject. 
        /// </returns>
        public virtual ICAEXObject AddElement(Type caexObjectType)
        {
            return null;
        }

        /// <summary>
        ///   returns the CAEXObject which is the model-object for this view-model
        /// </summary>
        /// <returns> The CAEX_ClassModel.CAEXBasicObject. </returns>
        public CAEXBasicObject GetCaexObject()
        {
            return this.CaexObject;
        }

        /// <summary>
        ///   loads the CaexObject into the represenation model
        /// </summary>
        public virtual void LoadCaexToRepresentationModel()
        {
        }

        /// <summary>
        ///   Validation is bound to the Property "LibraryObjectName" of the ViewModel, Notifying a PropertyChange will cause a revalidation
        /// </summary>
        public virtual void ReValidate()
        {
            this.OnNotifyPropertyChanged("ContainerName");
        }

        /// <summary>
        ///   saving a Node
        /// </summary>
        public virtual void Save()
        {
        }

        #endregion

        #region Methods

        /// <summary>
        /// PropertyChange Notification method
        /// </summary>
        /// <param name="propertyName">
        /// The property Name.
        /// </param>
        protected void OnNotifyPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

/*
        /// <summary>
        ///   If a Container gets a new Name, the ContainerName Property of this Object has to
        ///   raise a Property Changed Event.
        /// </summary>
        /// <param name="sender">
        ///   the sender Container
        /// </param>
        /// <param name="e">
        ///  event param
        /// </param>
        private void ContainerViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Name")
            {
                this.OnNotifyPropertyChanged("ContainerName");
            }
        }
*/

        #endregion

        /*
        /// <summary>
        /// The Difference Analysis uses an XNodeEqualityComparer. The XmlNode of the CAEXObject has to be transformed to an XNode to use
        ///   this Comparer
        /// </summary>
        /// <returns>
        /// The System.Xml.Linq.XNode.
        /// </returns>
        private XNode GetXElement()
        {
            var xDoc = new XDocument();

            using (var xmlWriter = xDoc.CreateWriter()) this.CaexObject.Node.WriteTo(xmlWriter);
            return xDoc.Root;
        }
*/

        /*
        /// <summary>
        /// TODO The nodes are equal.
        /// </summary>
        /// <param name="node">
        /// TODO The node.
        /// </param>
        /// <returns>
        /// The System.Boolean.
        /// </returns>
        private bool NodesAreEqual(XNode node)
        {
#if DEBUG
            var sb = new StringBuilder();
            var xws = new XmlWriterSettings { OmitXmlDeclaration = true, Indent = true };

            using (var xw = XmlWriter.Create(sb, xws))
            {
                xw.WriteStartElement("Root");
                this.OriginalNode.WriteTo(xw);
                node.WriteTo(xw);
                xw.WriteEndElement();
            }

            Console.WriteLine(sb.ToString());
#endif
            return XNode.EqualityComparer.Equals(this.OriginalNode, node);
        }
*/
    }
}